package com.ai.networking.netty.handler;

import com.ai.common.constant.AlgorithmConstant;
import com.ai.common.constant.Constants;
import com.ai.config.SpringContext;
import com.ai.entity.comm.SysNetty;
import com.ai.entity.network.L1ToIdentifySysMessage;
import com.ai.service.made.IL1SignalService;
import com.ai.service.networking.ISysNettyService;
import com.ai.entity.made.PlcLogs;
import com.ai.service.made.IPlcLogsService;
import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.AttributeKey;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 客户端触发操作
 */
@Slf4j
public class ServerHandler extends ChannelInboundHandlerAdapter {

    private SysNetty nettyServer;

    ISysNettyService sysNettyService;

    private IPlcLogsService iPlcLogsService;

    private IL1SignalService il1SignalService;

    public ServerHandler() {
    }

    public ServerHandler(String ip, int port) {
        sysNettyService = SpringContext.getBean(ISysNettyService.class);
        iPlcLogsService = SpringContext.getBean(IPlcLogsService.class);
        il1SignalService=SpringContext.getBean(IL1SignalService.class);

        nettyServer = sysNettyService.getNonNativeByIP(ip, port);
    }

    public static Map<String, ChannelHandlerContext> ctxMap = new ConcurrentHashMap<>(16);


    /**
     * 发送消息
     *
     * @param code
     * @param msg
     */
    public static void sendMessage(String code, String msg) {

        ByteBuf buf = Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8);
        ChannelHandlerContext ctx = ctxMap.get(code);
        ctx.writeAndFlush(buf);
        ctx.flush();

    }

    /**
     * 发送消息
     *
     * @param code
     * @param msg
     */
    public static void sendMessage(String code, byte[] msg) {


    }

    /**
     * 这个是对接 数据内容为utf8使用
     *
     * @param buf
     * @return
     */
    private static String getMessageStr(ByteBuf buf) {
        byte[] con = new byte[buf.readableBytes()];
        buf.readBytes(con);
        try {
            return new String(con, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }

    private byte[] getMessageBytes(ByteBuf buf) {
        int num = buf.readableBytes();

        byte[] req = new byte[num];
        buf.readBytes(req);
        return req;
    }

    /**
     * 移除已经失效的链接
     *
     * @param ctx
     */
    private void removeChannelMap(ChannelHandlerContext ctx) {
        for (String code : ctxMap.keySet()) {
            if (ctxMap.get(code) != null && ctxMap.get(code).equals(ctx)) {
                ctxMap.remove(code);
            }
        }
    }


    /**
     * channelAction
     * channel 通道 action 活跃的
     * 当客户端主动链接服务端的链接后，这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
     * 有客户端连接时，首先会触发Inbound处理器的注册以及激活工作，
     * 激活完成之后，就可以实现实现客户端与服务端消息的收发功能了
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("================13. 通过活跃中....========================");
        /**
         * 记录每一个信道----》服务端想要给客户端发送消息，就需要获取客户端对应的信道
         * 不同的客户端，对应的端口号不同，  通过端口号  定位到 信道，  从而获取到指定的客户端
         */
        ctxMap.put(nettyServer.getCode(), ctx);
        super.channelActive(ctx);
    }

    /**
     * channelInactive
     * channel 通道 Inactive 不活跃的
     * 当客户端主动断开服务端的链接后，这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据
     *
     * @param ctx
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.warn("--------Netty Disconnect Client IP is :" + ctx.channel().id().asShortText() + " "
                + ctx.channel().remoteAddress() + "--------");
        log.info("================9========================");
        removeChannelMap(ctx);
        super.channelInactive(ctx);
    }

    /**
     * 功能：读取服务器发送过来的信息
     *
     *  读取客户端发送来的消息
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//        log.info("================14. 通道读取服务器发送过来的信息========================");

        if (msg instanceof L1ToIdentifySysMessage){
            //处理L1发送来的信号
            L1ToIdentifySysMessage message = (L1ToIdentifySysMessage) msg;
            //判断是否为心跳信号
            if (message.isHeartBeat()){
//                log.info("  ========================  开始处理L1发送的心跳信号  ========================");
                //收到的为心跳电文，  给信道标记收到心跳的时间------最后一次读写时间
                ctx.channel().attr(AttributeKey.valueOf(Constants.ReadTime))
                        .set(System.currentTimeMillis());
            }else {
                log.info("  ========================  开始处理L1发送的操作信号  ========================");
                // : 2023-06-25 开始处理操作信号
                il1SignalService.receiveSignal(message);
            }


        }

    }

    /**
     * 客户端消息读取完毕
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//        log.info("================17. Netty读取信息已经完成啦！！========================");
        super.channelReadComplete(ctx);
    }


    /**
     *  发生了异常
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("--------Netty Exception ExceptionCaught : 【" + nettyServer.getCode() + "】 : "
                + cause.getMessage() + "=======================", cause);
        ctx.flush();
        ctx.close();
    }


    /**
      {
         "request" : {
            "createTime" : "2023-04-19 00:00:00",
            "functionType" : 1,
            "messageContent" : "DB1",
            "switchStatus" : 0
         },
         "type" : "101010101010"
      }
     * @param jsonMessage
     */
    public void persisPlcLog(String jsonMessage){
//       先处理传递的json数据，然后转换为json对象，通过键值对获取
        JSONObject o = JSONObject.parseObject(jsonMessage);
        String s =   o.getString("type");
        if (!("/plc/log".equals(s))){
            //接收到不是plc log
            return;
        }

        String request = o.getString("request");
//        String message = jsonMessage.substring(1, jsonMessage.lastIndexOf(","));
//        String substring = message.substring(message.indexOf("{")-1, message.lastIndexOf("}")+1);
        PlcLogs plcLogs = JSONObject.parseObject(request, PlcLogs.class);
        plcLogs.setCreateUser(nettyServer.getCode());//直接存储当前登录的用户名
        plcLogs.setUpdateUser(nettyServer.getCode());

        //填充公共字段
//        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//        if (authentication != null && authentication.getPrincipal() != null) {
//            String username = authentication.getName();
////            SysUser operatUser = this.selectByName(username);
////            if (null!=operatUser){
////                sysUser.setCreateUser(operatUser.getId());
////                sysUser.setUpdateUser(operatUser.getId());
////            }
////            // 可以根据username查询数据库获取更多用户信息
////            log.info(username);
//            plcLogs.setCreateUser(username);//直接存储当前登录的用户名
//            plcLogs.setUpdateUser(username);
//        }

        Date date = new Date();
        plcLogs.setCreateTime(date);
        plcLogs.setUpdateTime(date);

        iPlcLogsService.save(plcLogs);
    }
}
